from PyQt5 import QtWidgets, QtCore, uic, QtGui
import configparser
import sys
import prettytable

# from ui import *
import socket
import threading
from func import *
from color import *
import os

path = os.path.dirname(os.path.abspath(__file__))


class IMainWindow(QtWidgets.QMainWindow):
    def closeEvent(self, event):
        try:
            self.sock.sendto("exit".encode("utf-8"), self.addr)
        except TypeError:
            pass


class QSSLoader:
    def __init__(self):
        pass

    @staticmethod
    def read_qss_file(qss_file_name):
        with open(qss_file_name, 'r', encoding='UTF-8') as file:
            return file.read()


class EmittingStr(QtCore.QObject):
    textWritten = QtCore.pyqtSignal(str)  # 定义一个发送str的信号

    def write(self, text):
        self.textWritten.emit(str(text))


class Main(IMainWindow):
    def __init__(self, sock) -> None:
        super().__init__()
        uic.loadUi(f"{path}\\client.ui", self)
        self.sock = sock
        self.account = None
        self.IP = None
        self.Port = None
        self.addr = None
        self.ifsave = None
        sys.stdout = EmittingStr(textWritten=self.recvRender)
        self.login = False
        try:
            self.IP = getConfig("Internet", "ip")
            self.Port = int(getConfig("Internet", "port"))
            self.addr = (self.IP, self.Port)
            self.font_size = int(getConfig("Render", "font_size"))
            self.font_family = getConfig("Render", "font_family")
            self.theme = getConfig("Render", "theme")
            font = QtGui.QFont()
            font.setPointSize(self.font_size)
            font.setFamily(self.font_family)
            self.mapbox.setFont(font)
            self.outputbox.setFont(font)
            self.Infobox.setFont(font)
            self.lineEdit.setFont(font)
            if self.theme == "dark":
                style_file = "./themes/dark.qss"
                style_sheet = QSSLoader.read_qss_file(style_file)
                self.setStyleSheet(style_sheet)
            elif self.theme == "light":
                style_file = "./themes/light.qss"
                style_sheet = QSSLoader.read_qss_file(style_file)
                self.setStyleSheet(style_sheet)
            print(f"001{YELLOW}已读取配置文件, 连接到服务器{self.addr}{NORMAL}")
            print(f"001请输入{YELLOW}账户名{NORMAL}")
            self.ifsave = True
        except (configparser.NoOptionError, configparser.NoSectionError):
            f = open(f"{path}\config.ini", "w")
            f.close()
            print(f"001请输入{YELLOW}IP{NORMAL}")

    def send(self):
        global actions, unit
        command = self.lineEdit.text()
        command = command.lower()
        if self.login:
            if "set" not in command:
                if command == "w":  # WASD转NWSE
                    command = "go N"
                elif command == "a":
                    command = "go W"
                elif command == "s":
                    command = "go S"
                elif command == "d":
                    command = "go E"
                elif command == "exit":
                    self.sock.sendto("exit".encode("utf-8"), self.addr)
                    exit()
                elif command in actions:  # 快捷look
                    command = f"action {command} {unit}"

                self.sock.sendto(command.encode('utf-8'), self.addr)
                print(f"004{command}")
            elif "set " in command:
                command = command.replace("set ", "", 1)
                if "fontsize " in command:
                    font_size = int(command.replace("fontsize ", "", 1))
                    try:
                        font = QtGui.QFont()
                        font.setPointSize(font_size)
                        font.setFamily(self.font_family)
                        self.mapbox.setFont(font)
                        self.outputbox.setFont(font)
                        self.Infobox.setFont(font)
                        self.lineEdit.setFont(font)
                        writeConfig("Render", ["font_size"], [font_size])
                        print(f"001已将字体大小设置为{font_size}")
                    except TypeError:
                        print(f"001{RED}字体大小必须为数字！{NORMAL}")
                elif "theme " in command:
                    theme = command.replace("theme ", "", 1)
                    if theme == "dark":
                        self.load_theme("dark")
                    elif theme == "light":
                        self.load_theme("light")
                    writeConfig("Render", ["theme"], [theme])
                    self.theme = theme
        elif not self.IP:  # 输入的是IP
            self.IP = command
            print(f"001请输入{YELLOW}端口{NORMAL}")
        elif not self.Port:  # 输入的是端口
            self.Port = int(command)
            self.addr = (self.IP, self.Port)
            print(f"001是否保存？{YELLOW}Y/N (是/否){NORMAL}")
        elif not self.ifsave:
            if command.lower() == "y":
                # -------------------------配置初始化---------------------------#
                writeConfig("Internet", ["ip", "port"], [self.IP, self.Port])
                writeConfig(
                    "Render",
                    ["font_size", "font_family", "theme"],
                    [12, "SimHei", "light"],
                )
                # -------------------------配置初始化---------------------------#
            self.ifsave = True
            print(f"001请输入{YELLOW}账户名{NORMAL}")
        elif not self.account:  # 输入的是账户名
            self.account = command
            main_r = threading.Thread(
                target=recv,
                args=(self.sock, self.addr, self.account),
                daemon=True,
            )
            main_r.start()
            self.login = True

    def load_theme(self, theme):
        style_sheet = QSSLoader.read_qss_file(f"./themes/{theme}.qss")
        self.setStyleSheet(style_sheet)

    def recvRender(self, text):
        style = text[:3]
        text = text.replace(style, "")
        text = text.replace("  ", "　")  # 全角空格替换
        text = text.replace("\n", "<br>")  # \n-><br> (换行符)
        text = text.replace("\033[1;31m", "<span style='color:#c02c38'>")  # 红->高粱红
        text = text.replace('\033[1;32m', "<span style='color:#229453'>")  # 绿->孔雀绿
        text = text.replace('\033[0;33m', "<span style='color:#ffa60f'>")  # 黄->金叶黄
        text = text.replace('\033[1;34m', "<span style='color:#2376b7'>")  # 蓝->花青
        text = text.replace('\033[1;35m', "<span style='color:#7e1671'>")  # 紫->魏紫
        text = text.replace('\033[1;36m', "<span style='color:#63bbd0'>")  # 青->霁青
        text = text.replace('\033[1;30m', "<span style='color:#b2bbbe'>")  # 灰->星灰
        text = text.replace('\033[0m', "</span>")  # 无色
        match style:
            case "001":
                self.outputbox.append(
                    f"<div style='white-space:pre'><p>{text}</p></div>"
                )
            case "002":
                self.mapbox.clear()
                self.mapbox.append(f"<div style='white-space:pre'><p>{text}</p></div>")
            case "003":
                self.Infobox.clear()
                self.Infobox.append(
                    f"<div style='white-space:pre-warp'><p>{text}</p></div>"
                )
            case "004":
                # self.attributebox.clear()
                # self.attributebox.append(
                #    f"<div style='white-space:pre'><p>{text}</p></div>"
                # )
                pass

    def exit_(self):
        self.sock.sendto("exit".encode("utf-8"), self.addr)
        self.close()

    def relogin(self):
        self.sock.sendto("exit".encode("utf-8"), self.addr)
        self.account = ""
        print(f"001请输入{YELLOW}账户名{NORMAL}")

    def open_ini(self):
        os.startfile(f"{path}/config.ini")

    def attribute(self):
        try:
            self.sock.sendto("hp".encode("utf-8"), self.addr)
        except NameError:
            print(f"{RED}你还没登录呢{NORMAL}")


def cut(obj, sec):  # sourcery skip: instance-method-first-arg-name
    return [obj[i : i + sec] for i in range(0, len(obj), sec)]


def getConfig(section, key: str = None) -> str:
    '''
    获取[section]下[key]对应的值
    '''
    config = configparser.ConfigParser()
    config.read(f"{path}\config.ini", encoding='utf-8')  # 读取config
    if key != None:  # 指定了key
        return config.get(section, key)
    else:  # 未指定key（读取section下的所有key）
        return config.items(section)


def writeConfig(section: str, key: list, value: list) -> None:
    '''
    将[section]下[key]的值定为[value]
    若只填写section，则创建[section]
    若没有section，将自动创建section
    [key] -> list
    [value] -> list
    '''
    config = configparser.ConfigParser()  # 指定config读取器
    config.read(f'{path}\config.ini', encoding="utf-8")

    if not config.has_section(section):
        config.add_section(section)
    for i in range(len(key)):  # 依次对应填写
        config.set(section, str(key[i]), str(value[i]))  # 设置值
    config.write(open(f"{path}\config.ini", "w"))


def recv(sock=socket.socket, addr=tuple, name=""):
    '''
    一个UDP连接在接收消息前必须要让系统知道所占端口
    也就是需要send一次，否则win下会报错
    '''
    global actions, unit
    sock.sendto(name.encode('utf-8'), addr)  # 初始化连接发送名字
    print('已连接至服务器[]')
    while True:
        data = sock.recv(4096)
        message = data.decode('utf-8')
        style = message[:3]
        match style:
            case "001":
                print(f"001请输入{YELLOW}密码{NORMAL}：")
            case '002':
                print(f"001{YELLOW}登陆成功{NORMAL}")
            case '003':
                print(f"001检测到您还没有账号，已跳转到{YELLOW}注册{NORMAL}")
                print(f"001请输入{YELLOW}密码{NORMAL}：")
            case "004":
                print(f"001请{YELLOW}再次输入密码{NORMAL}:")
            case "005":
                print(f"001{YELLOW}注册成功，已自动登录{NORMAL}")
            case "006":
                print("001该账户没用创建角色，请新建角色进入游戏")
                print(f"001请输入{YELLOW}角色名（必须为中文）：{NORMAL}")
            case "007":
                print(f"001{YELLOW}创建成功，已自动进入游戏{NORMAL}")
            case "008":
                text = message.replace("008", '')  # text="[1,2,3]|["xxx","xxx","xxx"]
                text = text.split("|")  # text = ["[1,2,3]","["xxx","xxx","xxx"]"]
                characterID = eval(text[0])
                characterName = eval(text[1])
                print("001输入序号以选择角色, 输入new新建角色")
                for i in range(len(characterID)):
                    print(
                        "001[{ID}]   {Name}".format(
                            ID=characterID[i], Name=characterName[i]
                        )
                    )
            case "009":
                print("001欢迎回来, {Name}".format(Name=message.replace("009", "")))
            case '101':
                print(f"001{RED}密码错误{NORMAL}")
                print(f"001请重新输入{YELLOW}密码{NORMAL}：")
            case "102":
                print(f"001{RED}前后密码不一致{NORMAL}")
                print(f"001请重新输入{YELLOW}密码{NORMAL}：")
            case "103":
                print(f"001{RED}该账号已在另一客户端登录，三秒后自动退出{NORMAL}")
                exit()
            case "104":
                print(f"001{RED}角色名必须全部为中文{NORMAL}，请重新输入")
            case "106":
                print(f"003{YELLOW}那边似乎过不去了{NORMAL}")
            case "107":
                print(f"001{RED}指令参数错误 应为：go <方向>(n,s等){NORMAL}")
            case "108":
                item = message.replace("108", "")
                print(f"001{YELLOW}单位不存在：{item}{NORMAL}")
            case "109":
                print(f"{RED}指令缺少参数！(或许是空格打多了？){NORMAL}")
            case "200":
                print(f'001{message.replace("200", "")}')
            case "201":
                name, text = message.replace("201", '', 1).split("|", 1)  # ["名字","内容"]
                print(f"001{YELLOW}[{name}]:{NORMAL} {text}")
            case "202":
                Name = message.replace("202", "")
                print(f"001{YELLOW}[{Name}]退出了游戏{NORMAL}")
            case "203":
                Name = message.replace("203", "", 1)
                print(f"001{YELLOW}[{Name}]加入了游戏{NORMAL}")
            case "204":
                attribute = eval(message.replace("204", ""))
                print(f"001{attribute_formatter.formatAttribute(attribute)}")
            case "205":
                worldmap = []
                message = message.replace("205", "")
                worldmap_str, pos_str = message.split("|")
                worldmap = eval(worldmap_str)
                pos = eval(pos_str)
                print(
                    f"002{map_formatter.formatWorldMap(worldmap, ifAxis=True, pos=pos)}"
                )
            case "206":
                message = message.replace("206", "")
                name, info, if_exit, exit_objects, units = message.split("|")
                if_exit = eval(if_exit)
                exit_objects = eval(exit_objects)
                units = eval(units)
                print(
                    f"003{map_formatter.formatPlaceInfo(name, info, if_exit, exit_objects, units)}"
                )
            case "207":  # 单位信息
                message = message.replace("207", "")
                unit, info, actions, amount = message.split("|")
                actions = eval(actions)
                print(f"003{map_formatter.formatUnitInfo(unit, info ,actions ,amount)}")
            case "299":
                print(f"{YELLOW}账户已登出！{NORMAL}")
            case _:
                print(message)


if __name__ == '__main__':
    actions = []
    unit = ""
    # 获取UIC窗口操作权限
    app = QtWidgets.QApplication(sys.argv)
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    MainWindow = Main(s)
    # 调自定义的界面（即刚转换的.py对象
    MainWindow.show()
    sys.exit(app.exec())
